﻿using AIStudio.Service.Quartz.Extensions;
using AutoMapper;
using Coldairarrow.Business.Quartz_Manage;
using Coldairarrow.Util;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using Quartz.Logging;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace AIStudio.Service.Quartz
{
    public static class QuartzNETExtension
    {
        private static SynchronizedCollection<Quartz_TaskDTO> _taskList = new SynchronizedCollection<Quartz_TaskDTO>();
        private static IQuartz_TaskBusiness _quartz_TaskBusiness;
        /// <summary>
        /// 初始化作业
        /// </summary>
        /// <param name="applicationBuilder"></param>
        /// <param name="env"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseQuartz(this IApplicationBuilder applicationBuilder)
        {
            IServiceProvider services = applicationBuilder.ApplicationServices;

            ISchedulerFactory _schedulerFactory = services.GetService<ISchedulerFactory>();
            _quartz_TaskBusiness = services.GetService<IQuartz_TaskBusiness>();
            IMapper _mapper = services.GetService<IMapper>();

            LogProvider.SetCurrentLogProvider(new CustomConsoleLogProvider());

            int errorCount = 0;
            string errorMsg = null;

            var list = _mapper.Map<SynchronizedCollection<Quartz_TaskDTO>>(_quartz_TaskBusiness.GetList());

            list.ForEach(async x =>
            {
                try
                {
                    _taskList.Add(x);
                    var result = await _schedulerFactory.AddJob(x, true);

                }
                catch (Exception ex)
                {
                    errorCount = +1;
                    errorMsg += $"作业:{x?.TaskName},异常：{ex.Message}";
                }
            });

            string content = $"任务启动-成功:{_taskList.Count - errorCount}个,失败{errorCount}个,异常信息：{errorMsg ?? "无"}";
            FileQuartz.WriteDebug(content);

            return applicationBuilder;
        }

        /// <summary>
        /// 获取所有的作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <returns></returns>
        public static async Task<List<Quartz_TaskDTO>> GetJobs(this ISchedulerFactory schedulerFactory)
        {
            List<Quartz_TaskDTO> list = new List<Quartz_TaskDTO>();
            try
            {
                IScheduler _scheduler = await schedulerFactory.GetScheduler();
                var groups = await _scheduler.GetJobGroupNames();
                foreach (var groupName in groups)
                {
                    foreach (var jobKey in await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName)))
                    {
                        Quartz_TaskDTO taskOptions = _taskList.Where(x => x.GroupName == jobKey.Group && x.TaskName == jobKey.Name)
                            .FirstOrDefault();
                        if (taskOptions == null)
                            continue;

                        var triggers = await _scheduler.GetTriggersOfJob(jobKey);
                        foreach (ITrigger trigger in triggers)
                        {
                            DateTimeOffset? dateTimeOffset = trigger.GetPreviousFireTimeUtc();
                            if (dateTimeOffset != null)
                            {
                                taskOptions.LastRunTime = Convert.ToDateTime(dateTimeOffset.ToString());
                            }
                        }
                        list.Add(taskOptions);
                    }
                }
            }
            catch (Exception ex)
            {
                FileQuartz.WriteError("获取作业异常：" + ex.Message + ex.StackTrace);
            }
            return list;
        }

        /// <summary>
        /// 添加作业
        /// </summary>
        /// <param name="taskOption"></param>
        /// <param name="schedulerFactory"></param>
        /// <param name="init">是否初始化,否=需要重新生成配置文件，是=不重新生成配置文件</param>
        /// <param name="jobAction"></param>
        /// <returns></returns>
        public static async Task<JobExcuteResult> AddJob(this ISchedulerFactory schedulerFactory, Quartz_TaskDTO taskOption, bool init = false, JobAction jobAction = JobAction.新增)
        {
            try
            {
                var validExpression = taskOption.Interval.IsValidExpression();
                if (!validExpression.Success)
                    return validExpression;


                if (!init)
                {
                    var result = taskOption.Exists();
                    if (!result.Success)
                        return result;

                    await SaveJob(taskOption);
                    _taskList.Add(taskOption);
                }

                var type = GlobalJob.Instance.AllTypes.FirstOrDefault(p => p.Name == taskOption.ApiUrl);

                IJobDetail job = null;
                if (type != null)
                {
                    job = JobBuilder.Create(type)
                   .WithIdentity(taskOption.TaskName, taskOption.GroupName)
                  .Build();
                }
                else
                {
                    job = JobBuilder.Create<HttpResultful>()
                    .WithIdentity(taskOption.TaskName, taskOption.GroupName)
                   .Build();
                }

                ITrigger trigger = TriggerBuilder.Create()
                   .WithIdentity(taskOption.TaskName, taskOption.GroupName)
                   .StartAt(CronHelper.StartTime2DateTimeOffset(taskOption.StartTime) ?? SystemTime.UtcNow())
                   .EndAt(CronHelper.DateTime2DateTimeOffset(taskOption.EndTime))
                   .WithDescription(taskOption.Describe)
                   .WithCronSchedule(taskOption.Interval)
                   .Build();

                IScheduler scheduler = await schedulerFactory.GetScheduler();
                await scheduler.ScheduleJob(job, trigger);
                if (taskOption.Status == (int)TriggerState.Normal)
                {
                    await scheduler.Start();
                    FileQuartz.WriteJobAction(jobAction, taskOption?.TaskName, taskOption.GroupName, "启动成功");
                }
                else
                {
                    await scheduler.PauseTrigger(trigger.Key);
                    FileQuartz.WriteJobAction(jobAction, taskOption?.TaskName, taskOption.GroupName, $"未启动原因,状态为:{taskOption.Status}");
                }

                return new JobExcuteResult { Success = true, Message = taskOption.NextRunTime };
            }
            catch (Exception ex)
            {
                return new JobExcuteResult { Success = false, Message = ex.Message };
            }
        }

        /// <summary>
        /// 移除作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOption"></param>
        /// <returns></returns>
        public static Task<JobExcuteResult> Remove(this ISchedulerFactory schedulerFactory, Quartz_TaskDTO taskOption)
        {
            return schedulerFactory.TriggerAction(JobAction.删除, taskOption);
        }

        /// <summary>
        /// 更新作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOption"></param>
        /// <returns></returns>
        public static Task<JobExcuteResult> Update(this ISchedulerFactory schedulerFactory, Quartz_TaskDTO taskOption)
        {
            return schedulerFactory.TriggerAction(JobAction.修改, taskOption);
        }

        /// <summary>
        /// 暂停作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOption"></param>
        /// <returns></returns>
        public static Task<JobExcuteResult> Pause(this ISchedulerFactory schedulerFactory, Quartz_TaskDTO taskOption)
        {
            return schedulerFactory.TriggerAction(JobAction.暂停, taskOption);
        }

        /// <summary>
        /// 启动作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOption"></param>
        /// <returns></returns>
        public static Task<JobExcuteResult> Start(this ISchedulerFactory schedulerFactory, Quartz_TaskDTO taskOption)
        {
            return schedulerFactory.TriggerAction(JobAction.开启, taskOption);
        }

        /// <summary>
        /// 立即执行一次作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOption"></param>
        /// <returns></returns>
        public static Task<JobExcuteResult> Run(this ISchedulerFactory schedulerFactory, Quartz_TaskDTO taskOption)
        {
            return schedulerFactory.TriggerAction(JobAction.立即执行, taskOption);
        }


        /// <summary>
        /// 触发新增、删除、修改、暂停、启用、立即执行事件
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="action"></param>
        /// <param name="taskOption"></param>
        /// <returns></returns>
        public static async Task<JobExcuteResult> TriggerAction(this ISchedulerFactory schedulerFactory, JobAction action, Quartz_TaskDTO taskOption)
        {
            JobExcuteResult result = null;
            string errorMsg = "";
            try
            {
                var memoryOption = _taskList.FirstOrDefault(p => p.Id == taskOption.Id);
                string taskName = memoryOption == null ? taskOption.TaskName : memoryOption.TaskName;
                var groupName = memoryOption == null ? taskOption.GroupName : memoryOption.GroupName;

                JobKey jobKey = null;
                ITrigger trigger = null;

                IScheduler scheduler = await schedulerFactory.GetScheduler();
                List<JobKey> jobKeys = (await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName))).ToList();
                if (jobKeys == null || jobKeys.Count() == 0)
                {
                    result = new JobExcuteResult { Success = false, Message = $"未找到分组[{groupName}],请确定任务是不是已经完成！" };
                }
                else
                {
                    jobKey = null;
                    foreach (var key in jobKeys)
                    {
                        if ((await scheduler.GetTriggersOfJob(key)).Any(x => (x as CronTriggerImpl).Name == taskName))
                        {
                            jobKey = key;
                            break;
                        }
                    }
                    if (jobKey == null)
                    {
                        result = new JobExcuteResult { Success = false, Message = $"未找到触发器[{taskName}],请确定任务是不是已经完成！" };
                    }
                    else
                    {
                        var triggers = await scheduler.GetTriggersOfJob(jobKey);
                        trigger = triggers?.Where(x => (x as CronTriggerImpl).Name == taskName).FirstOrDefault();

                        if (trigger == null)
                        {
                            result = new JobExcuteResult { Success = false, Message = $"未找到触发器[{taskName}],请确定任务是不是已经完成！" };
                        }
                    }
                }

                if (action == JobAction.暂停 || action == JobAction.停止 || action == JobAction.开启 || action == JobAction.立即执行)
                {
                    if (trigger == null)
                    {
                        if (action == JobAction.开启)//停止以后再开启就找不到了
                        {
                            action = JobAction.修改;
                        }else
                        {
                            return result;
                        }
                    }
                }
              
                switch (action)
                {
                    case JobAction.删除:
                    case JobAction.修改:
                        if (taskOption.ForbidEdit)
                            return new JobExcuteResult { Success = false, Message = "禁止修改删除任务" };

                        if (trigger != null)//先从Quartz中移除
                        {
                            await scheduler.PauseTrigger(trigger.Key);
                            await scheduler.UnscheduleJob(trigger.Key);// 移除触发器
                            await scheduler.DeleteJob(trigger.JobKey);
                        }

                        result = await schedulerFactory.ModifyTaskEntity(taskOption, action);
                        break;
                    case JobAction.暂停:
                    case JobAction.停止:
                    case JobAction.开启:
                        if (taskOption.ForbidEdit)
                            return new JobExcuteResult { Success = false, Message = "禁止操作任务" };

                        result = await schedulerFactory.ModifyTaskEntity(taskOption, action);
                        if (action == JobAction.暂停)
                        {
                            await scheduler.PauseTrigger(trigger.Key);
                        }
                        else if (action == JobAction.开启)
                        {
                            await scheduler.ResumeTrigger(trigger.Key);
                            //   await scheduler.RescheduleJob(trigger.Key, trigger);
                        }
                        else
                        {
                            await scheduler.Shutdown();
                        }
                        break;
                    case JobAction.立即执行:
                        await scheduler.TriggerJob(jobKey);
                        result = new JobExcuteResult { Success = true };
                        break;
                }
                return result ?? new JobExcuteResult { Success = true, Message = $"作业{action.ToString()}成功" };
            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
                return new JobExcuteResult { Success = false, Message = ex.Message };
            }
        }


        public static async Task<JobExcuteResult> ModifyTaskEntity(this ISchedulerFactory schedulerFactory, Quartz_TaskDTO taskOption, JobAction action)
        {
            Quartz_TaskDTO options = null;

            //移除以前的配置
            var memoryOption = _taskList.FirstOrDefault(p => p.Id == taskOption.Id);
            if (memoryOption != null)
            {
                _taskList.Remove(memoryOption);
            }

            JobExcuteResult result = null;
            switch (action)
            {
                case JobAction.删除:
                    await DeleteJob(taskOption);
                    FileQuartz.WriteJobAction(action, taskOption.TaskName, taskOption.GroupName, "操作对象：" + JsonConvert.SerializeObject(taskOption));
                    result = new JobExcuteResult { Success = true};
                    break;
                case JobAction.修改:
                    //生成任务并添加新配置
                    result = await schedulerFactory.AddJob(taskOption, false, JobAction.修改);
                    break;
                case JobAction.暂停:
                case JobAction.开启:
                case JobAction.停止:
                case JobAction.立即执行:
                    _taskList.Add(memoryOption);
                    if (action == JobAction.暂停)
                    {
                        options.Status = (int)TriggerState.Paused;
                    }
                    else if (action == JobAction.停止)
                    {
                        options.Status = (int)action;
                    }
                    else
                    {
                        options.Status = (int)TriggerState.Normal;
                    }

                    await SaveJob(options);
                    FileQuartz.WriteJobAction(action, taskOption.TaskName, taskOption.GroupName, "操作对象：" + JsonConvert.SerializeObject(taskOption));
                    result = new JobExcuteResult { Success = true };
                    break;
            }
            //生成配置文件
         
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>通过作业上下文获取作业对应的配置参数
        /// <returns></returns>
        public static Quartz_TaskDTO GetTaskOptions(this IJobExecutionContext context)
        {
            AbstractTrigger trigger = (context as JobExecutionContextImpl).Trigger as AbstractTrigger;
            Quartz_TaskDTO taskOptions = _taskList.Where(x => x.TaskName == trigger.Name && x.GroupName == trigger.Group).FirstOrDefault();
            return taskOptions ?? _taskList.Where(x => x.TaskName == trigger.JobName && x.GroupName == trigger.JobGroup).FirstOrDefault();
        }

        public static Quartz_TaskDTO GetTaskOptions(this ITrigger trigger)
        {
            Quartz_TaskDTO taskOptions = _taskList.Where(x => x.TaskName == trigger.JobKey.Name && x.GroupName == trigger.JobKey.Group).FirstOrDefault();
            return taskOptions;
        }

        public static Quartz_TaskDTO GetTaskOptions(this TriggerKey trigger)
        {
            Quartz_TaskDTO taskOptions = _taskList.Where(x => x.TaskName == trigger.Name && x.GroupName == trigger.Group).FirstOrDefault();
            return taskOptions;
        }

        public static Quartz_TaskDTO GetTaskOptions(this JobKey trigger)
        {
            Quartz_TaskDTO taskOptions = _taskList.Where(x => x.TaskName == trigger.Name && x.GroupName == trigger.Group).FirstOrDefault();
            return taskOptions;
        }

        /// <summary>
        /// 作业是否存在
        /// </summary>
        /// <param name="taskOption"></param>
        /// <param name="init">初始化的不需要判断</param>
        /// <returns></returns>
        public static JobExcuteResult Exists(this Quartz_TaskDTO taskOption)
        {
            if (_taskList.Any(x => x.TaskName == taskOption.TaskName && x.GroupName == taskOption.GroupName))
            {
                return new JobExcuteResult
                {
                    Success = false,
                    Message = $"作业:{taskOption.TaskName},分组：{taskOption.GroupName}已经存在"
                };
            }
            return new JobExcuteResult { Success = true };
        }

        public static JobExcuteResult IsValidExpression(this string cronExpression)
        {
            try
            {
                CronTriggerImpl trigger = new CronTriggerImpl();
                trigger.CronExpressionString = cronExpression;
                DateTimeOffset? date = trigger.ComputeFirstFireTimeUtc(null);
                return new JobExcuteResult { Success = date != null, Message = (date == null ? $"请确认表达式{cronExpression}是否正确!" : "") };
            }
            catch (Exception e)
            {
                return new JobExcuteResult { Success = false, Message = $"请确认表达式{cronExpression}是否正确!{e.Message}" };
            }
        }

        public static JobExcuteResult GetNextRunTime(this string cronExpression, DateTime? startTime, DateTime? endTime)
        {
            try
            {
                ITrigger trigger = TriggerBuilder.Create()
                .StartAt(CronHelper.StartTime2DateTimeOffset(startTime) ?? SystemTime.UtcNow())
                .EndAt(CronHelper.DateTime2DateTimeOffset(endTime))
                .WithCronSchedule(cronExpression)
                .Build();

                IList<DateTimeOffset> dates = TriggerUtils.ComputeFireTimes(trigger as IOperableTrigger, null, 5).ToList();
                return new JobExcuteResult { Success = true, Message = String.Join(",", dates.Select(p => TimeZoneInfo.ConvertTimeFromUtc(p.DateTime, TimeZoneInfo.Local))) };
            }
            catch (Exception ex)
            {
                return new JobExcuteResult { Success = false, Message = $"请确认表达式{cronExpression}是否正确！{ex.Message}" };
            }
        }


        public static async Task SaveJob(List<Quartz_TaskDTO> taskList)
        {
            foreach (var task in taskList)
            {
                var result = GetNextRunTime(task.Interval, task.StartTime, task.EndTime);
                if (result.Success)
                {
                    task.NextRunTime = result.Message;
                }

                if (task.CreateTime == DateTime.MinValue)
                {
                    task.CreateTime = DateTime.Now;
                    await _quartz_TaskBusiness.InsertAsync(task);
                }
                else
                {
                    task.ModifyTime = DateTime.Now;
                    await _quartz_TaskBusiness.UpdateAsync(task);
                }
            }
        }

        public static async Task SaveJob(Quartz_TaskDTO task)
        {
            await SaveJob(new List<Quartz_TaskDTO> { task });
        }

        public static async Task DeleteJob(Quartz_TaskDTO task)
        {
            await _quartz_TaskBusiness.DeleteAsync(task);
        }
    }
}
